<!-- 本组件提供自动上传一个图像文件到服务器的功能
常用于上传标题图、栏目题图、广告图、头像等单张图片
上传成功后，服务器返回的完整数据格式是：
{
  ExtName: ".jpg"
  FileNameClient: "QQ图片20211209084707.jpg"
  Message: "OK"
  MimeType: "image/jpeg"
  Url: "/upload-file/image/2022/01/03/20220103110722323_QQ图片20211209084707.jpg"
  UrlThumb: "/upload-file/image/2022/01/03/20220103110722323_QQ图片20211209084707_tn.jpg"
  UrlWm: ""
  uuid: "c92edbb2d72b4963a88dc36c4f99f45f"
}

以文章标题图为例，上传完成后，返回给调用方的数据格式是：
{
  Url: "/upload-file/image/2022/01/03/20220103110722323_QQ图片20211209084707.jpg"
  UrlThumb: "/upload-file/image/2022/01/03/20220103110722323_QQ图片20211209084707_tn.jpg"
  UrlWm: ""
  uuid: "c92edbb2d72b4963a88dc36c4f99f45f"
}

添加文章内容页将 uuid 和 urlThumb 存入到文章数据表里
当文章在前端显示时，前端读取到文章标题图的uuid 和urlThumb，补充上后端的域名，构建图片的url并显示

编辑文章内容页时，读取到标题图的 uuid 和 urlThumb ，传入上传组件
上传组件的 fileList 用后端域名 + urlThumb 构建url来显示这张图片，并可以用uuid删除这张图片，然后重新传过

使用方法：
1、导入本组件
import TheUploader from '@/components/UploadImage2Local'
2、定义本组件
components: { TheUploader }
3、html中调用本组件
<TheUploader @setUploadResult="getUploadResult($event)" />
4、父组件中定义获取上传结果的方法
getUploadResult(val) {
  formData.urlTitlePic = JSON.stringify(val)
}
-->
<template>
  <div class="wrapper-uploader">
    <el-upload
      ref="refUploader"
      :action="urlActionRemote"
      :data="paramsJson"
      :headers="headerJwt"
      :class="{ 'is-hide': flagHideUploader }"
      :limit="1"
      :multiple="false"
      :show-file-list="true"
      list-type="picture-card"
      :file-list="selectedFileList"
      :on-change="onChange"
      :before-upload="onBeforeUpload"
      :on-progress="onProgress"
      :on-success="onSuccess"
      :on-error="onError"
      :on-remove="onRemove"
      :on-preview="onPreview"
    >
      <el-icon><Plus /></el-icon>
    </el-upload>
    <el-dialog v-model="flagDialogVisiblePreview" title="图片预览">
      <img style="width: 100%" :src="urlImagePreview" alt="" />
    </el-dialog>
  </div>
</template>

<script>
import { getJwtFromCookie } from '@/utils/jwt'
import { getLStorageObj } from '@/utils/web-storage'

import { isBlankAry } from '@/utils/array'
import { isAllowedFile, isAllowedSizeByExt, getSizeLimitByExt } from '@/utils/file'
import { toastSuccess, toastError } from '@/utils/toast'

import router from '@/router'
import store from '@/store'
import { defineComponent, ref, onMounted, watch, computed } from 'vue'
export default defineComponent({
  name: 'UploadImageCom',
  props: {
    selectedFile: {
      type: Object,
      default: () => {},
    },
    adminId: {
      type: Number,
      default: 0,
    },
    taskId: {
      type: Number,
      default: 0,
    },
  },
  emit: ['update:setUploadResult'],
  setup(props, { emit }) {
    const uploadConfig = ref(null)
    // 当选择满了图片后隐藏 选择图片按钮 要用到的判断变量
    const flagHideUploader = ref(false)
    // 设置显示图片的前缀
    const urlRoot = ref(process.env.VUE_APP_URL_UPLOAD_SHOW_UUID)
    // 执行上传到服务器的action的url
    const urlActionRemote = ref(process.env.VUE_APP_URL_UPLOAD_ACTION)
    const flagDialogVisiblePreview = ref(false)
    const urlImagePreview = ref('')
    // 如果是编辑状态，用下面两个变量存储已上传的图片并显示
    const fileUploaded = ref({})
    const selectedFileList = ref([])

    const headerJwt = computed(() => {
      return { authorization: 'Bearer ' + getJwtFromCookie() }
    })
    const paramsJson = computed(() => {
      return { AdminId: props.adminId, TaskId: props.taskId, FormData: null }
    })

    // 获取上传设置
    const getConfigUpload = () => {
      uploadConfig.value = getLStorageObj('UploadConfig')
      // 检验上传配置是否已设置，如果没有，跳回登录页
      if (!uploadConfig.value) {
        toastError('读取上传配置出错！请重新登录')
        router.push({ path: '/login' })
      }
    }

    // 文件状态改变时的钩子，添加文件、上传成功和上传失败时都会被调用，移除文件时不会调用
    // 经过调试，选了3个文件，这个方法会执行3次
    // 要执行多次，所以验证文件不放在这里
    const onChange = (fileList) => {
      console.log('onChange() fileList', fileList)
    }
    // 自动上传时，在上传前进行格式校验和大小校验
    const onBeforeUpload = (rawFile) => {
      console.log('onBeforeUpload() rawFile', rawFile)
      // 验证格式
      if (!isAllowedFile(rawFile, uploadConfig.value.allowedExtImg)) {
        toastError('文件 [ ' + rawFile.name + ' ] 的格式不正确!')
        return false
      }
      // 验证大小
      if (!isAllowedSizeByExt(rawFile, uploadConfig.value)) {
        const msg =
          '文件 [ ' + rawFile.name + ' ] 的大小不能超过' + getSizeLimitByExt(rawFile, uploadConfig.value) + 'K!'
        toastError(msg)
        return false
      }
      return true
    }
    // 上传过程中
    const onProgress = (file) => {
      console.log('onProgress() file：', file)
      // clog('onProgress的event:', event)
      // clog('onProgress的file:', file)
      // 当前上传的进度
      // const loadProgress = Math.floor(event.percent)
    }
    // 文件自动上传成功时的钩子
    const onSuccess = (response, file) => {
      console.log('onSuccess() response：', response)
      if (response && response.data.Message === 'OK') {
        // 上传成功
        flagHideUploader.value = true
        const uploadResult = response.data
        const returnObj = {
          uuid: '',
          url: '',
          urlThumb: '',
          urlWm: '',
        }
        returnObj.uuid = uploadResult.uuid
        // 上传成功，必有 Url，但是不一定有 UrlThumb 和 UrlWm
        returnObj.url = uploadResult.Url
        returnObj.urlThumb = uploadResult.UrlThumb
        returnObj.urlWm = uploadResult.UrlWm
        // if (uploadResult.UrlThumb && uploadResult.UrlThumb.length > 0) {
        //   returnObj.urlThumb = uploadResult.UrlThumb
        // }
        // if (uploadResult.UrlWm && uploadResult.UrlWm.length > 0) {
        //   returnObj.urlWm = uploadResult.UrlWm
        // }
        returnUploadResult(returnObj)
        toastSuccess('上传成功！')
      } else {
        if (response.data.message) {
          toastError(response.data.message)
        } else {
          toastError('服务器错误！上传' + file.name + '失败！')
        }
        // 上传失败，返回空对象
        returnUploadResult({})
      }
    }
    // 文件上传失败时的钩子
    const onError = (err) => {
      console.log('onError() err：', err)
      // clog('onError 的 err:', err)
      toastError(err)
      flagHideUploader.value = false
    }
    // 文件列表移除文件时的钩子，onBeforeUpload() 失败时也触发此方法
    const onRemove = (file) => {
      console.log('onRemove 时 file:', file)
      // console.log('onRemove 时 fileList:', fileList)
      flagHideUploader.value = false
      if (file.response) {
        // 如果有response，说明是重新上传成功的图片，此时删除服务器上的文件
        store
          .dispatch('sys_ul_file/delByUuid', file.response.data.uuid)
          .then(() => {
            returnUploadResult('')
            toastSuccess('删除成功!')
          })
          .catch((error) => {
            console.log('服务器删除文件返回错误:', error)
          })
      } else {
        returnUploadResult('')
      }
    }
    // 上传成功后的缩略图上有个放大镜，点击放大预览图片
    const onPreview = (file) => {
      console.log('onPreview() file:', file)
      // 设置弹出对话框中显示图片的img标签的url
      urlImagePreview.value = file.url
      flagDialogVisiblePreview.value = true
    }

    const returnUploadResult = (val) => {
      console.log('returnUploadResult() val', val)
      // 调用本组件时，设置 @setUploadResult="getUploadResult($event)"
      // 定义一个名称为 setUploadResult 的方法，方法的参数值就是这里返回的上传结果
      emit('setUploadResult', val)
    }

    onMounted(() => {
      getConfigUpload()
    })

    watch(
      () => props.selectedFile,
      (val) => {
        if (isBlankAry(val)) {
          flagHideUploader.value = false
          selectedFileList.value = []
        } else {
          flagHideUploader.value = true
          fileUploaded.value.url = urlRoot.value + val[0].uuid
          fileUploaded.value.uuid = val[0].uuid
          selectedFileList.value.push(fileUploaded.value)
        }
      },
      {
        deep: true,
        immediate: true,
      }
    )

    return {
      uploadConfig,
      flagHideUploader,
      urlRoot,
      urlActionRemote,
      urlImagePreview,
      flagDialogVisiblePreview,
      fileUploaded,
      selectedFileList,
      headerJwt,
      paramsJson,
      getConfigUpload,
      onChange,
      onBeforeUpload,
      onProgress,
      onSuccess,
      onError,
      onRemove,
      onPreview,
      returnUploadResult,
    }
  },
})
</script>

<style lang="scss" scoped>
:deep(.el-upload--picture-card) {
  width: 50px;
  height: 50px;
  line-height: 50px;
}

:deep(.el-upload-list__item) {
  width: 120px;
  height: 120px;
  margin: 0;
}
.is-hide {
  :deep(.el-upload) {
    display: none;
  }
}
</style>
